#include "PreThreadServ.h"

const int  MAX_CONNECT = 10;
static int run_flag = 1;
static pthread_t *thread_ids;
static int nthreads;
pthread_mutex_t    clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t      clifd_cond = PTHREAD_COND_INITIALIZER;

int main(int argc, char** argv)
{
    printf("welcome to thread server\n");
    if (argc == 4 && '1' == *argv[1])
    {
        serv1(argc, argv);
    }
    else if(argc == 3)
    {
        serv(argc, argv);
    }
    else
    {
        err_quit("usage : %s [version] [host] [port]", *argv);
    }
    

    pause();//使得父进程在子进程退出后再退出，仅仅便于计算程序运行时间
    printf("end of main\n");
    return 0;
}

void* child_make(void* param)
{
    if (NULL != param)
    {
        int listenfd;
        socklen_t addrlen;
        memcpy(&listenfd, param, sizeof(listenfd));
        memcpy(&addrlen, param + sizeof(listenfd), sizeof(addrlen));
        while (1 == run_flag)
        {
            printf("begin to accept\n");
            struct sockaddr cliaddr;
            int connfd = accept(listenfd, &cliaddr, &addrlen);
            printf("current thread id :%lx\n", pthread_self());
            if (connfd > 0)
            {
                web_child(connfd);
                close(connfd);
            }
            else if(EINTR == errno)
            {
                printf("continue to accept\n");
                continue;
            }
            else
            {
                printf("accept error. errno :%d\tmsg :%s\n", errno, strerror(errno));
                break;
            }
        }
    }
    printf("end of child_make\n");
    return NULL;
}


void web_child(int connfd)
{
    printf("str_echo :0x%x\n", connfd);
    size_t n;
    char buf[MAXLEN] = {0};

    while (1 == run_flag)
    {
        memset(buf, 0, sizeof(buf));
        n = read(connfd, buf, MAXLEN-1);
        if(n > 0)
        {
            write(connfd, buf, n);
            printf("server :%s\n", buf);
        }
        else if(n < 0 && errno == EINTR)
        {
            printf("continue to read\n");
            continue;
        }
        else
        {
            printf("unknown error, n :%ld\terrno :0x%x\n",n, errno);
            break;
        }
    }
    printf("end of web_chlid\n");
}

void serv(int argc, char** argv)
{
    int listenfd;
    socklen_t addrlen;

    if (argc == 1)
    {
        if ((listenfd = tcp_listen(NULL, NULL, &addrlen)) < 0)
        {
            err_quit("listen error");
        }
    }
    else if (argc == 4)
    {
        if ((listenfd = tcp_listen(argv[1], argv[2], &addrlen)) < 0)
        {
            err_quit("listen errot");
        }
    }
    else
    {
        err_quit("usage : prethreadserv [version] [host] [port]");
    }
    
    thread_ids = (pthread_t*) malloc(MAX_CONNECT * sizeof(pthread_t));
    int param_bytes = sizeof(listenfd) + sizeof(addrlen);
    char params[MAX_CONNECT][param_bytes];
    for (size_t i = 0; i < MAX_CONNECT; i++)
    {
        char* param = params[i];
        memcpy(param, &listenfd, sizeof(listenfd));
        memcpy(param + sizeof(listenfd), &addrlen, sizeof(addrlen));
        int res = pthread_create(&thread_ids[i], NULL, child_make, param);
        if(0 != res)
        {
            printf("create error, res :%d\n", res);
        }
        else
        {
            pthread_detach(thread_ids[i]);
            printf("thread id :%lx\n", thread_ids[i]);
        }
    }
}

void serv1(int argc, char** argv)
{
    printf("serv1 start\n");
    int i, listenfd, connfd;
    socklen_t addrlen, clilen;
    struct sockaddr* cliaddr;

    if (2 == argc)
    {
        listenfd = tcp_listen(NULL, NULL, &addrlen);
        if (listenfd < 0)
        {
            err_quit("listen error!");
        }
    }
    else if(4 == argc)
    {
        listenfd = tcp_listen(argv[2], argv[3], &addrlen);
        if (listenfd < 0)
        {
            err_quit("listen error!");
        }
    }
    else
    {
        err_quit("usgae : prethreadserv [version] [host] [port]");
    }

    cliaddr = (struct sockaddr*) malloc(addrlen);
    nthreads = 10;
    tptr = (Thread*) malloc(sizeof(Thread) * nthreads);
    iget = iput = 0;

    for (size_t i = 0; i < nthreads; i++)
    {
        thread_make(i);
    }
    //signal(SIGINT, sig_int);
    clilen = addrlen;
    while (1)
    {
        connfd = accept(listenfd, cliaddr, &clilen);
        pthread_mutex_lock(&clifd_mutex);
        clifd[iput] =connfd;
        if (++iput == MAXCLI)
        {
            iput = 0;
        }
        if (iput == iget)
        {
            err_quit("iput = iget = %d", iput);
        }
        pthread_cond_signal(&clifd_cond);
        pthread_mutex_unlock(&clifd_mutex);
    }
}


void thread_make(int i)
{
    int ret = pthread_create(&tptr[i].thread_tid, NULL, &thread_main, (void*)i);
    if (ret == 0)
    {
        printf("create thread :%d\n", i);
    }
    else
    {
        printf("create error :%d", ret);
    }
}


void* thread_main(void* arg)
{
    int connfd;
    printf("thread %d starting\n", (int)arg);

    while (1)
    {
        pthread_mutex_lock(&clifd_mutex);
        while (iget == iput)
        {
            pthread_cond_wait(&clifd_cond, &clifd_mutex);
        }
        connfd = clifd[iget];
        if (++iget == MAXCLI)
        {
            iget = 0;
        }
        pthread_mutex_unlock(&clifd_mutex);
        tptr[(int)arg].thread_count++;
        printf("thread %d processing\n", (int)arg);
        web_child(connfd);
        close(connfd);
    }
}